/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.component.spreadsheet;

import javafx.collections.ObservableList;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;
import org.apache.commons.lang3.StringUtils;
import org.controlsfx.control.spreadsheet.*;

import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 单元格类型控件
 * @author zpy
 * @version 1.0.0
 */
public abstract class PesSheetCellType {

    public static final TextAreaType TEXT_AREA = new TextAreaType();

    public static class TextAreaType extends SpreadsheetCellType<String> {

        public TextAreaType(){
            this(new DefaultStringConverter());
        }

        public TextAreaType(StringConverter<String> converter){
            super(converter);
        }

        @Override
        public String toString(){
            return "textArea";
        }

        @Override
        public boolean match(Object value, Object... options) {
            return true;
        }

        /**
         * Creates a cell that hold a String at the specified position, with the
         * specified row/column span.
         *
         * @param row
         *            row number
         * @param column
         *            column number
         * @param rowSpan
         *            rowSpan (1 is normal)
         * @param columnSpan
         *            ColumnSpan (1 is normal)
         * @param value
         *            the value to display
         * @return a {@link SpreadsheetCell}
         */
        public SpreadsheetCell createCell(final int row, final int column, final int rowSpan, final int columnSpan,
                                          final String value) {
            SpreadsheetCell cell = new SpreadsheetCellBase(row, column, rowSpan, columnSpan, this);
            cell.setItem(value);
            return cell;
        }

        @Override
        public SpreadsheetCellEditor createEditor(SpreadsheetView view) {
            return new SpreadsheetCellEditor.TextAreaEditor(view);
        }

        @Override
        public String convertValue(Object value) {
            String convertedValue = converter.fromString(value == null ? null : value.toString());
            if (convertedValue == null || convertedValue.equals("")) { //$NON-NLS-1$
                return null;
            }
            return convertedValue;
        }

        @Override
        public String toString(String item) {
            return converter.toString(item);
        }

    }

    public static <T> ComboBoxType<T> LIST(final ObservableList<T> items) {
        return new ComboBoxType<T>(items);
    }

    public static class ComboBoxType<T> extends SpreadsheetCellType<String> {
        protected final ObservableList<T> items;

        public ComboBoxType(final ObservableList<T> items) {
            super(new DefaultStringConverter() {
                @Override
                public String fromString(String str) {
                    if (str != null && items.stream().anyMatch(i->StringUtils.equals(i.toString(),str))) {
                        return str;
                    } else {
                        return null;
                    }
                }
            });
            this.items = items;
        }
        @Override
        public String toString() {
            return "comboBox"; //$NON-NLS-1$
        }

        /**
         * Creates a cell that hold a String at the specified position, with the
         * specified row/column span.
         *
         * @param row
         *            row number
         * @param column
         *            column number
         * @param rowSpan
         *            rowSpan (1 is normal)
         * @param columnSpan
         *            ColumnSpan (1 is normal)
         * @param value
         *            the value to display
         * @return a {@link SpreadsheetCell}
         */
        public SpreadsheetCell createCell(final int row, final int column, final int rowSpan, final int columnSpan,
                                          String value) {
            SpreadsheetCell cell = new SpreadsheetCellBase(row, column, rowSpan, columnSpan, this);
            if (items != null && items.size() > 0) {
                if (value != null && items.stream().anyMatch(i->StringUtils.equals(i.toString(),value))) {
                    cell.setItem(value);
                } else {
                    cell.setItem("");
                }
            }
            return cell;
        }

        @Override
        public SpreadsheetCellEditor createEditor(SpreadsheetView view) {
            var editor = new PesComboBoxCellEditor<>(view, items);
            editor.getEditor().getStyleClass().add("pes-result-combo-box");
            return editor;
        }

        @Override
        public boolean match(Object value, Object... options) {
            return Objects.nonNull(value) && items.stream().anyMatch(i -> StringUtils.equals(i.toString(), value.toString()));
        }

        @Override
        public String convertValue(Object value) {
            return converter.fromString(value == null ? null : value.toString());
        }

        @Override
        public String toString(String item) {
            return converter.toString(item);
        }

    }

    public static <T> CheckComboBoxType<T> CHECK_LIST(final ObservableList<T> items,final String splitChar) {
        return new CheckComboBoxType<>(items,splitChar);
    }

    public static class CheckComboBoxType<T> extends SpreadsheetCellType<String> {
        protected final ObservableList<T> items;
        protected final String splitChar;

        public CheckComboBoxType(final ObservableList<T> items,String splitChar) {
            super(new DefaultStringConverter() {
                @Override
                public String fromString(String str) {
                    if (StringUtils.isNotBlank(str)) {
                        return Arrays.stream(StringUtils.split(str,splitChar))
                                .filter(v->items.stream().anyMatch(i->StringUtils.equals(v,i.toString())))
                                .collect(Collectors.joining(splitChar));
                    } else {
                        return null;
                    }
                }
            });
            this.items = items;
            this.splitChar = splitChar;
        }

        @Override
        public String toString() {
            return "checkComboBox"; //$NON-NLS-1$
        }

        /**
         * Creates a cell that hold a String at the specified position, with the
         * specified row/column span.
         *
         * @param row
         *            row number
         * @param column
         *            column number
         * @param rowSpan
         *            rowSpan (1 is normal)
         * @param columnSpan
         *            ColumnSpan (1 is normal)
         * @param value
         *            the value to display
         * @return a {@link SpreadsheetCell}
         */
        public SpreadsheetCell createCell(final int row, final int column, final int rowSpan, final int columnSpan,
                                          String value) {
            SpreadsheetCell cell = new SpreadsheetCellBase(row, column, rowSpan, columnSpan, this);
            if (items != null && items.size() > 0) {
                if (StringUtils.isNotBlank(value)) {
                    String content = Arrays.stream(StringUtils.split(value,this.splitChar))
                            .filter(v->items.stream().anyMatch(i->StringUtils.equals(v,i.toString())))
                            .collect(Collectors.joining(this.splitChar));
                    cell.setItem(content);
                } else {
                    cell.setItem("");
                }
            }
            return cell;
        }

        @Override
        public SpreadsheetCellEditor createEditor(SpreadsheetView view) {
            return new PesCheckComboBoxCellEditor<>(view, items, splitChar);
        }

        @Override
        public boolean match(Object value, Object... options) {
            return Objects.nonNull(value) && Arrays.stream(StringUtils.split(value.toString(),this.splitChar))
                    .allMatch(v->items.stream().anyMatch(i->StringUtils.equals(v,i.toString())));
        }

        @Override
        public String convertValue(Object value) {
            return converter.fromString(value == null ? null : value.toString());
        }

        @Override
        public String toString(String item) {
            return converter.toString(item);
        }

    }

}